/* 
 *File: eval.c
 * ------------------------
 * This file implements the eval interface.
 *
 * Authors: Olof Ivarsson och Johanna Carlsson
 */

#include <stdio.h>
#include "genlib.h"
#include "strlib.h"
#include "eval.h"
#include "env.h"
#include "symtab.h"

#define MAX_CALLS 1000

/* Function prototypes */
static valueADT evalCompound(expADT exp, environmentADT env);
static valueADT evalIf(expADT exp, environmentADT env);
static valueADT evalCall(expADT exp, environmentADT env);
static valueADT evalFunc(expADT exp, environmentADT env);
static valueADT getIdentifier(environmentADT env, string name);
static valueADT evalExp(expADT exp, environmentADT env);
static bool checkIf(char relOp, int lhs, int rhs);


/* static variables */
static int numCalls = 0;


/*
 * Function: eval
 * Usage: value = eval(exp, env);
 * ------------------------------
 * This function starts the evalutation of an expression. It then returns the
 * evalutated expression as a valueT.
 */
valueADT eval(expADT exp, environmentADT env){
	valueADT value;

	numCalls = 0;

	return evalExp(exp,env);
}

/*
 * Function: evalExp
 * Usage: value = evalExp(exp, env);
 * ------------------------------
 * This function handles which function to call, depending on the 
 * expressions type.
 */
static valueADT evalExp(expADT exp, environmentADT env){
	if(numCalls > MAX_CALLS){
		Error("Stack overflow. Too deep recursion.");
	}

	numCalls++;

	switch(ExpType(exp)){
	case IfExp: return evalIf(exp, env);
	case CallExp: return evalCall(exp,env);
	case ConstExp: return NewIntegerValue(ExpInteger(exp));
	case IdentifierExp: return getIdentifier(exp, env);
	case CompoundExp: return evalCompound(exp, env);
	case FuncExp: return evalFunc(exp, env);
	default:
		Error("Unexpected ExpType...");
	}
}

/*
 * Function: getIdentifier
 * Usage: value = getIdentifier(exp, env);
 * ------------------------------
 * This function gets the iddentifier of the exp. 
 */
static valueADT getIdentifier(expADT exp, environmentADT env){
	valueADT value;

	value = GetIdentifierValue(env, ExpIdentifier(exp));
	
	return evalExp(GetFuncValueBody(value),(environmentADT)GetFuncValueClosure(value));
}

/*
 * Function: evalCompound
 * Usage: value = evalCompound(exp, env);
 * ------------------------------
 * This function caluculates the compound expression of exp.
 */
static valueADT evalCompound(expADT exp, environmentADT env){
	char op;
	valueADT lhs, rhs;
	int rhsValue;

	op = ExpOperator(exp);
	
	rhs = evalExp(ExpRHS(exp), env);
	lhs = evalExp(ExpLHS(exp), env);

	if(ValueType(lhs) != IntValue && ValueType(rhs) != IntValue)
		Error("Illegal value type in eval compound.");

	rhsValue = GetIntValue(rhs);
	switch(op){
		case '+': return NewIntegerValue(GetIntValue(lhs) + rhsValue);
		case '-': return NewIntegerValue(GetIntValue(lhs) - rhsValue);
		case '*': return NewIntegerValue(GetIntValue(lhs) * rhsValue);
		case '/': if(rhsValue == 0)
						Error("Division by 0.");
					return NewIntegerValue(GetIntValue(lhs) / rhsValue);
		default: Error("Illegal operator");
	}
}

/*
 * Function: evalIf
 * Usage: value = evalIf(exp, env);
 * ------------------------------
 * This function handels the evaluation of an if expression.
 */
static valueADT evalIf(expADT exp, environmentADT env){
	char relOp;
	valueADT lhs,rhs;

	relOp = GetIfRelOp(exp);
	rhs = evalExp(GetIfRHSExpression(exp), env);
	lhs = evalExp(GetIfLHSExpression(exp), env);

	if(ValueType(lhs) != IntValue && ValueType(rhs) != IntValue)
		Error("Illegal value type in eval If.");
	
	if(checkIf(relOp, GetIntValue(lhs), GetIntValue(rhs))) 
		return evalExp(GetIfThenPart(exp), env);
	else
		return evalExp(GetIfElsePart(exp), env);	
}

/*
 * Function: checkIf
 * Usage: if(checkIf(relop, lhs, rhs);
 * ------------------------------
 * This function check lhs and rhs depending on relop.
 */
static bool checkIf(char relOp, int lhs, int rhs){
	switch(relOp){
		case '=':
			return (lhs == rhs); 
		case '<':
			return (lhs < rhs);
		case '>':
			return (lhs > rhs);
		default:
			Error("Illegal realOperator");
	}
}

/*
 * Function: evalCall
 * Usage: value = evalCall(expADT exp, environmentADT env);
 * ------------------------------
 * This function evaluates a function call.
 */
static valueADT evalCall(expADT exp, environmentADT env){
	expADT func,arg;
	string callBodyArg;
	valueADT funcValue;
	environmentADT newEnv;
	
	func = GetCallExp(exp);
	arg = GetCallActualArg(exp);
	funcValue = evalExp(func,env);

	newEnv = NewClosure((environmentADT)GetFuncValueClosure(funcValue));
		
	if(ValueType(funcValue) != FuncValue)
		Error("Illegal value type in evalCall");

	callBodyArg = GetFuncValueFormalArg(funcValue);	
	DefineIdentifier(newEnv,callBodyArg,arg,env);

	return evalExp(GetFuncValueBody(funcValue),newEnv);
}

/*
 * Function: evalFunc
 * Usage: value = evalFunc(expADT exp, environmentADT env);
 * ------------------------------
 * This function evaluates a function expression.
 */
static valueADT evalFunc(expADT exp, environmentADT env){
	string arg;
	expADT body;
		
	arg = GetFuncFormalArg(exp);
	body = GetFuncBody(exp);

	return NewFuncValue(arg, body, env);
}